#pragma once

#include <functional>
#include <limits>
#include <memory>
#include "Logger.h"
#include "Macros.h"

namespace zeroplus::mq
{

    // Timer
    class Timer final
    {
    public:
        using Callback = std::function<void()>;
        enum Priority : uint16_t
        {
            kLowestPriority = 0,
            kTimerPriority = 50,
            kNormalPriority = 100,
            kHighestPriority = std::numeric_limits<uint16_t>::max(),
        };
        static constexpr int kInfinite{-1};

        explicit Timer();

        explicit Timer(class MessageLoop *message_loop);

        explicit Timer(class MessageLoop *message_loop, uint32_t interval_ms, int32_t loop_count = kInfinite,
                       const Callback &callback = nullptr);

        explicit Timer(uint32_t interval_ms, int32_t loop_count = kInfinite, const Callback &callback = nullptr);

        ~Timer();

        static void call_once(class MessageLoop *message_loop, uint32_t interval_ms, const Callback &callback);

        [[nodiscard]] bool is_active() const;

        [[nodiscard]] uint32_t get_interval() const;

        [[nodiscard]] int32_t get_loop_count() const;

        [[nodiscard]] int32_t get_remain_loop_count() const;

        [[nodiscard]] uint64_t get_invoke_count() const;

        [[nodiscard]] uint16_t get_priority() const;

        [[nodiscard]] class MessageLoop *get_message_loop() const;

        bool attach(class MessageLoop *message_loop);

        bool detach();

        void start(const Callback &callback = nullptr);

        void restart();

        void stop();

        void set_interval(uint32_t interval_ms);

        void set_loop_count(int32_t loop_count);

        void set_callback(const Callback &callback);

    private:
        void clear();

        void force_to_start();

        void sub_remain_loop_count() const;

        void set_invoke_count(uint64_t invoke_count) const;

        void set_priority(uint16_t priority);

        uint64_t get_start_time() const;

        bool is_once_type() const;

        const Callback &get_callback() const;

        friend class MessageLoop;
        std::unique_ptr<struct TimerImpl> impl_;
        static constexpr uint32_t kMinInterval{100'000U};
        MQ_DISALLOW_COPY_AND_ASSIGN(Timer)
    };
} // namespace zeroplus::mq
